import React, {Component} from 'react'
import {MarkdownPreview} from 'react-marked-markdown';
// import py1_1 from '../static/py1_1.md'
// import marked from 'marked';

class Py22 extends Component {
    constructor(props) {
        super(props);
        this.state = {
           value: `

到目前为止，我们处理的数据大部分都是数字或数值（countable value）。大多数情况下，
我们只是简单地存储数据，没有分析数据。在这一章里，我们将尝试探索英语这个复杂的
主题。 

当你在 Google 的图片搜索里输入“cute kitten”时，Google 怎么会知道你要搜索什么呢？
其实这个词组与可爱的小猫咪是密切相关的。当你在 YouTube 搜索框中输入“dead parrot”
的时候，YouTube 怎么会知道要推荐一些 Monty Python 乐团的幽默短剧呢？那是因为每个
上传的视频里都带有标题和简介文字。

其实，输入“deceased bird monty python”这类短语时，即使页面上没有单词“deceased”
或“bird”，也会立即显示同样的“Dead Parrot”幽默短剧。Google 知道“hot dog”是一种
食物，“boiling puppy”却是另一种完全不同的东西。它究竟是怎么实现的呢？其实这一切
都是统计学在起作用！

虽然你可能认为自己的项目和文本分析没有任何关系，但是理解文本分析的原理对各种机
器学习场景都是非常有用的，而且还可以进一步提高自己利用概率论和算法知识对现实问
题进行抽象建模的能力。

例如，Shazam 音乐雷达是一种可以识别出一段音频中包含哪首歌的服务，即使音频中包
含了环绕的噪声或失真也没问题。Google 正在通过图片内容识别自动增加识别文字。 2 比
如对已知的热狗图片和其他热狗图片进行对比，搜索引擎就可以通过不断地学习掌握热狗
的特征，然后对其他图片进行模式识别，判断图片是不是热狗

> 8.1　概括数据

在第 7 章里，我们介绍过如何把文本内容分解成 n-gram 模型，或者说是 n 个单词长度的
词组。从最基本的功能上说，这个集合可以用来确定这段文字中最常用的单词和短语。另
外，还可以提取原文中那些最常用的短语周围的句子，对原文进行看似合理的概括。

我们即将用来做数据归纳的文字样本源自美国第九任总统威廉 ·亨利 ·哈里森的就职演
说。哈里森的总统生涯创下美国总统任职历史的两个记录：一个是最长的就职演说，另一
个是最短的任职时间——32 天。

我们将用他的总统就职演说（\http://pythonscraping.com/files/inaugurationSpeech.txt）的全文
作为这一章许多示例代码的数据源。

简单修改一下我们在第 7 章里用过的 n-gram 模型，就可以获得 2-gram 序列的频率数据，

然后我们用 Python 的 operator 模块对 2-gram 序列的频率字典进行排序：
     
    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    import re
    import string
    import operator

    def cleanInput(input):
        input = re.sub('\\n+', " ", input).lower()
        input = re.sub('\\[[0-9]*\\]', "", input)
        input = re.sub(' +', " ", input)
        input = bytes(input, "UTF-8")
        input = input.decode("ascii", "ignore")
        cleanInput = []
        input = input.split(' ')
        for item in input:
            item = item.strip(string.punctuation)
            if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'):
                cleanInput.append(item)
        return cleanInput

    def ngrams(input, n):
        input = cleanInput(input)
        output = {}
        for i in range(len(input)-n+1):
            ngramTemp = " ".join(input[i:i+n])
            if ngramTemp not in output:
                output[ngramTemp] = 0
            output[ngramTemp] += 1
        return output

    content = str(
            urlopen("http://pythonscraping.com/files/inaugurationSpeech.txt").read(),
                'utf-8')
    ngrams = ngrams(content, 2)
    sortedNGrams = sorted(ngrams.items(), key = operator.itemgetter(1), reverse=True)
    print(sortedNGrams)
     
输出结果的一部分是：
     
    [('of the', 213), ('in the', 65), ('to the', 61), ('by the', 41), ('t
    he constitution', 34), ('of our', 29), ('to be', 26), ('from the', 24
    ), ('the people', 24), ('and the', 23), ('it is', 23), ('that the', 2
    3), ('of a', 22), ('of their', 19)
  
这些 2-gram 序列中，“the constitution”像是演说的主旨，“of the”“in the”和“to the”看
起来并不重要。怎么才能用准确的方式去掉这些不想要的词呢？

前人已经仔细地研究过这些“有意义的”单词和“没意义的”单词的差异了，他们的工作
可以帮助我们完成过滤工作。美国杨百翰大学的语言学教授 Mark Davies 一直在维护当代
美式英语语料库（Corpus of Contemporary American English，\http://corpus.byu.edu/coca/），
里面包含了 1990~2012 年美国畅销作品里的超过 4.5 亿个单词。

最常用的 5000 个单词列表可以免费获取，作为一个基本的过滤器来过滤最常用的 2-gram
序列绰绰有余。其实只用前 100 个单词就可以大幅改善分析结果，我们增加一个 isCommon
函数来实现：
   
    def isCommon(ngram):
        commonWords = ["the", "be", "and", "of", "a", "in", "to", "have", "it",
            "i", "that", "for", "you", "he", "with", "on", "do", "say", "this",
            "they", "is", "an", "at", "but","we", "his", "from", "that", "not",
            "by", "she", "or", "as", "what", "go", "their","can", "who", "get",
            "if", "would", "her", "all", "my", "make", "about", "know", "will",
            "as", "up", "one", "time", "has", "been", "there", "year", "so",
            "think", "when", "which", "them", "some", "me", "people", "take",
            "out", "into", "just", "see", "him", "your", "come", "could", "now",
            "than", "like", "other", "how", "then", "its", "our", "two", "more",
            "these", "want", "way", "look", "first", "also", "new", "because",
            "day", "more", "use", "no", "man", "find", "here", "thing", "give",
            "many", "well"]
        for word in ngram:
            if word in commonWords:
                return True
        return False
    
这样处理之后，就可以得到在样本文字中出现的频率不低于三次的 2-gram 序列，如下所示：
    
    ('united states', 10), ('executive department', 4), ('general governm
    ent', 4), ('called upon', 3), ('government should', 3), ('whole count
    ry', 3), ('mr jefferson', 3), ('chief magistrate', 3), ('same causes'
    , 3), ('legislative body', 3)
   
效果看着不错，列表中的前两项是“United States”和“executive department”，和我们对
就职演说的期待是一样的。

这里需要注意的是，我们是用比较新的常用词列表过滤结果的，这对 1841 年写出来的文
字来说可能不是非常合适。但是，因为我们只用了列表里前 100 个单词——我们姑且可以
认为，随着年代的变化，这 100 个单词应该比列表最后的 100 个单词要更具稳定性——而
且我们也获得了满意的结果，所以好像也不必挖掘或创建一组 1841 年最常用的单词列表
（虽然这样的努力可能会很有趣）。

现在一些核心的主题词已经从文本中抽取出来了，它们怎么帮助我们归纳这段文字呢？一
种方法是搜索包含每个核心 n-gram 序列的第一句话，这个方法的理论是英语中段落的首句
往往是对后面内容的概述。前五个 2-gram 序列的搜索结果是：

* The Constitution of the United States is the instrument containing this grant of power to the
several departments composing the government.
* Such a one was afforded by the executive department constituted by the Constitution.
* The general government has seized upon none of the reserved rights of the states.
* Called from a retirement which I had supposed was to continue for the residue of my life
to fill the chief executive office of this great and free nation, I appear before you, fellow-
citizens, to take the oaths which the constitution prescribes as a necessary qualification for
the performance of its duties; and in obedience to a custom coeval with our government
and what I believe to be your expectations I proceed to present to you a summary of the
principles which will govern me in the discharge of the duties which I shall be called upon
to perform.
* The presses in the necessary employment of the government should never be used to clear
the guilty or to varnish crime.

当然，这些估计还不能马上发布到 CliffsNotes 上面，但是考虑到全文原来一共有 217 句
话，而这里的第四句话（“Called from a retirement...”）已经把主题总结得很好了，作为初
稿应该能凑合。

### 维基百科六度分割：终结篇

在第 3 章，我们创建了收集从凯文 ·贝肯开始的维基词条链接的爬虫，最后存储在数据库
里。为什么这里又把这个游戏搬出来？因为它体现了一种从一个页面指向另一个页面的链
接路径选择问题（即找出 \https://en.wikipedia.org/wiki/Kevin_Bacon 和 \https://en.wikipedia.org/wiki/Eric_Idle 链接路径），这和从上面的马尔可夫链里找出一个单词到另一个单词的路
径的问题是一样的。这类问题被称为有向图（directed graph）问题，其中 A → B 连通，并
不意味着 B → A 同样连通。单词“football”后面可能经常跟的是单词“player”，但是单
词“player”后面却很少跟着单词“football”。虽然凯文 ·贝肯（Kevin Bacon）的维基词条
连接到他的老家费城（Philadelphia），但是费城的维基百科词条里却没有连接他的链接。

相反，原来的凯文 ·贝肯六度分割游戏是一个无向图（undirected graph）问题。例如凯
文· 贝肯和朱莉娅 ·罗伯茨（Julia Roberts）共同演过电影《别闯阴阳界》（Flatliners，
1990），因此凯文 ·贝肯词条通过《别闯阴阳界》的维基词条会链接到朱莉娅 ·罗伯茨词
条，而朱莉娅 ·罗伯茨词条也会通过《别闯阴阳界》的维基词条链接到凯文 ·贝肯词条，
两者的关系是相互的（就是没有“方向”性）。在计算机科学中，无向图问题相比有向图
问题不太常见，两者都属于计算难题。

虽然解决这两类问题和对应的多个分支问题的方法有很多，但是在寻找有向图的最短路径
问题中，即找出维基百科中凯文 ·贝肯词条和其他词条之间最短链接路径的方法中，效果
最好且最常用的一种方法是广度优先搜索（breadth-first search）。

广度优先搜索算法的思路是优先搜寻直接连接到起始页的所有链接（而不是找到一个链接
就纵向深入搜索）。如果这些链接不包含目标页面（你想要找的词条），就对第二层的链
接——连接到起始页的页面的所有链接——进行搜索。这个过程不断重复，直到达到搜索
深度限制（本例中使用的层数限制是 6 层）或者找到目标页面为止。

用第 5 章里获得的链接数据表，实现一个完整的广度优先搜索算法，代码如下所示：
  
    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    import pymysql

    conn = pymysql.connect(host='127.0.0.1', unix_socket='/tmp/mysql.sock',
                    user='root', passwd=None, db='mysql', charset='utf8')
    cur = conn.cursor()
    cur.execute("USE wikipedia")

    class SolutionFound(RuntimeError):
        def __init__(self, message):
            self.message = message
            
    def getLinks(fromPageId):
        cur.execute("SELECT toPageId FROM links WHERE fromPageId = %s", (fromPageId))
        if cur.rowcount == 0:
            return None
        else:
            return [x[0] for x in cur.fetchall()]
            
    def constructDict(currentPageId):
        links = getLinks(currentPageId)
        if links:
            return dict(zip(links, [{}]*len(links)))
        return {}

    # 链接树要么为空，要么包含多个链接
    def searchDepth(targetPageId, currentPageId, linkTree, depth):
        if depth == 0:
            # 停止递归，返回结果
            return linkTree
        if not linkTree:
            linkTree = constructDict(currentPageId)
            if not linkTree:
                # 若此节点页面无链接，则跳过此节点
                return {}
        if targetPageId in linkTree.keys():
            print("TARGET "+str(targetPageId)+" FOUND!")
            raise SolutionFound("PAGE: "+str(currentPageId))
        for branchKey, branchValue in linkTree.items():
            try:
                # 递归建立链接树
                linkTree[branchKey] = searchDepth(targetPageId, branchKey,
                                    branchValue, depth-1)
            except SolutionFound as e:
                print(e.message)
                raise SolutionFound("PAGE: "+str(currentPageId))
        return linkTree
    try:
        searchDepth(134951, 1, {}, 4)
        print("No solution found")
    except SolutionFound as e:
        print(e.message)
 
这里函数 getLinks 和 constructDict 是辅助函数，用来从数据库里获取给定页面的链接，
然后把链接转换成字典。主函数 searchDepth 会递归地执行，同时构建和搜索链接树，一
次搜索一层。其运行规则如下所示。

* 如果递归限制已经到达（即程序已经调用过很多次），就停止搜索，返回结果。
* 如果函数获取的链接字典是空的，就对当前页面的链接进行搜索。如果当前页面也没链
接，就返回空链接字典。
* 如果当前页面包含我们搜索的页面链接，就把页面 ID 复制到递归的栈顶，然后抛出一
个异常，显示页面已经找到。递归过程中的每个栈都会打印当前页面 ID，然后抛出异
常显示页面已经找到，最终打印在屏幕上的就是一个完整的页面 ID 路径列表。
* 如果链接没找到，把递归限制减一，然后调用函数搜索下一层链接。

下面是凯文 ·贝肯词条（在数据库中页面 ID 为 1）和埃里克 ·艾德尔词条（在数据库中页
面 ID 为 78520）的链接路径：
    
    TARGET 134951 FOUND!
    PAGE: 156224
    PAGE: 155545
    PAGE: 3
    PAGE: 1
   
对应的链接名称是：Kevin Bacon → San Diego Comic Con International → Brian Froud →
Terry Jones → Eric Idle。

除了解决“六度分隔”问题和对句子中的一个词后面跟哪个词问题进行建模，有向图和无
向图还可以对网络数据采集中的许多场景进行建模。例如，一个网站与其他网站的链接关
系是什么？一篇学术论文与其他学术论文之间的引用关系如何？零售网站上哪些产品应该
捆绑销售？这个链接的强度是什么？这个链接是双向链接（reciprocal）吗？

了解这些基础知识对建模、可视化以及基于采集数据进行预测都非常有用。

> 8.3　自然语言工具包

到目前为止，本章主要讨论对文本中所有单词的统计分析。哪些单词使用得最频繁？哪些
单词用得最少？一个单词后面跟着哪几个单词？这些单词是如何组合在一起的？我们应该
做却还没做的事情，是理解每个单词的具体含义。

自然语言工具包（Natural Language Toolkit，NLTK）就是这样一个 Python 库，用于识别和
标记英语文本中各个词的词性（parts of speech）。这个项目于 2000 年创建，经过 15 年的
发展，由来自世界各地的几十个开发者共同努力维护。虽然它的功能非常丰富（有几本书
专门介绍 NLTK），但这一节只重点介绍几个用法。

> 8.3.1　安装与设置

NLTK 模块的安装方法和其他 Python 模块一样，要么从 NLTK 网站直接下载安装包进行
安装，要么用其他几个第三方安装器通过关键词“nltk”安装。详细的安装教程，请参考
NLTK 网站（http://www.nltk.org/install.html）。

模块安装之后，可以下载 NLTK 自带的文本库，这样你就可以非常轻松地实验 NLTK 的功
能。在 Python 命令行输入下面的命令即可：
 
    >>> import nltk
    >>> nltk.download()
  
两行命令会打开 NLTK 的下载器

推荐你安装所有的包。要下载的每个文件都是非常小的文本；你永远也不会知道后面会用
到哪一个，而且任何时候都可以轻易地卸载它们。

> 8.3.2　用NLTK做统计分析

NLTK 很擅长生成一些统计信息，包括对一段文字的单词数量、单词频率和单词词性的统
计。如果你只需要做一些简单直接的计算（比如，一段文字中不重复单词的数量），导入
NLTK 模块就太大材小用了——它是一个非常大的模块。但是，如果你还需要对文本做一
些更有深度的分析，那么里面有许多函数可以帮你实现任何需要的统计指标。

用 NLTK 做统计分析一般是从 Text 对象开始的。 Text 对象可以通过下面的方法用简单的
Python 字符串来创建：
    
    from nltk import word_tokenize
    from nltk import Text

    tokens = word_tokenize("Here is some not very interesting text")
    text = Text(tokens)
    
word_tokenize 函数的参数可以是任何 Python 字符串。如果你手边没有任何长字符串，但
是还想尝试一些功能，在 NLTK 库里已经内置了几本书，可以用 import 函数导入：
   
    from nltk.book import *
   
这样会加载九本书：
    
    *** Introductory Examples for the NLTK Book ***
    Loading text1, ..., text9 and sent1, ..., sent9
    Type the name of the text or sentence to view it.
    Type: 'texts()' or 'sents()' to list the materials.
    text1: Moby Dick by Herman Melville 1851
    text2: Sense and Sensibility by Jane Austen 1811
    text3: The Book of Genesis
    text4: Inaugural Address Corpus
    text5: Chat Corpus
    text6: Monty Python and the Holy Grail
    text7: Wall Street Journal
    text8: Personals Corpus
    text9: The Man Who Was Thursday by G . K . Chesterton 1908
     
在后面的例子，我们都用 text6，“Monty Python and the Holy Grail”（一部 1975 年电影的剧本）。

文本对象可以像普通的 Python 数组那样操作，好像它们就是一个包含文本里所有单词的数
组。用这个属性，你可以统计文本中不重复的单词，然后与总单词数据进行比较：
 
    >>> len(text6)/len(words)
    7.833333333333333
    前面的数据表明剧本中每个单词平均被使用了八次。你还可以将文本对象放到一个频率分
    布对象 FreqDist 中，查看哪些单词是最常用的，以及单词的频率是多少。
    >>> from nltk import FreqDist
    >>> fdist = FreqDist(text6)
    >>> fdist.most_common(10)
    [(':', 1197), ('.', 816), ('!', 801), (',', 731), ("'", 421), ('[', 3
    19), (']', 312), ('the', 299), ('I', 255), ('ARTHUR', 225)]
    >>> fdist["Grail"]
    34
    
因为这是一个剧本，所以剧本中创作的一些角色会显示出来。例如，全部大写的
“ARTHUR”频繁地出现，因为它会出现在亚瑟王（King Arthur）每一句台词的前面。另
外，分号（:）也出现在每一行的开头，当作分隔符把人物的姓名和人物的台词分开。根据
这个特征，我们可以看到这个电影剧本一共有 1197 句台词！

在上一章我们已经用过的 2-gram 模型，在 NLTK 中称作 bigrams（你可能会听到有人把
3-gram 模型叫作“trigrams”，但是我个人还是更喜欢用 n-gram 而不是 bigram 或 trigram）。
你可以用 NLTK 非常轻松地创建并搜索一个 2-gram 模型：
     
    >>> from nltk import bigrams
    >>> bigrams = bigrams(text6)
    >>> bigramsDist = FreqDist(bigrams)
    >>> bigramsDist[("Sir", "Robin")]
    18
 
为了搜索 2-gram 序列“Sir Robin”，我们需要把它分解成一个数组 ("Sir", "Robin") ，用
来匹配这个 2-gram 序列在频率分布中的表现方式。还有一个 trigrams 模块，它的工作方
式完全相同。对于更一般的情形，你还可以导入 ngrams 模块：
   
    >>> from nltk import ngrams
    >>> fourgrams = ngrams(text6, 4)
    >>> fourgramsDist = FreqDist(fourgrams)
    >>> fourgramsDist[("father", "smelt", "of", "elderberries")]
    1
  
这个 ngrams 函数被用来把文本对象分解成任意规模的 n-gram 序列，第二个参数决定规模
大小。在这个例子中，我把文本分解成了 4-gram。然后，我就可以查出“father smelt of
elderberries”这个短语在剧本中只出现了一次。

频率分布、文本对象和 n-gram 还可以整合在一个循环中进行迭代。例如，下面的程序就是
打印文本中所有以“coconut”开头的 4-gram 序列：
   
    from nltk.book import *
    from nltk import ngrams
    fourgrams = ngrams(text6, 4)
    for fourgram in fourgrams:
    if fourgram[0] == "coconut":
    print(fourgram)
    
NLTK 库设计了许多不同的工具和对象来组织、统计、排序和度量大段文字的含义。尽
管我们只是了解了 NLTK 函数用法的皮毛，但是大多数工具都设计得非常好，而且熟悉
Python 的人很容易操作它们。

> 8.3.3　用NLTK做词性分析

到现在为止，我们只是基于拼写方式对比和分类遇到的所有单词，并没有区分同义词或
语境。

虽然有人可能会认为同义词不处理也基本没什么问题，但是如果你看到了它们使用的频
率，可能会吓一跳。大多数以英语为母语的人可能不会注意到两个词互为同义词，也很少
会考虑同一个词在不同的语境中可能会导致意思混乱。

“He was objective in achieving his objective of writing an objective philosophy, primarily using
verbs in the objective case”（在实现他写作一本客观哲学书的目标时，他是客观的，因为他
在描述客观情况时主要用动词）这句话容易被人类理解，但是网络爬虫可能会认为是同样
的单词（objective）被用了四次，进而简单地忽略这四个单词各自不同的含义。

除了理清句子中各个词的词性，还要区分出某句话中一个单词的用法。例如，你可能会
需要查找一些普通英文单词组成的公司名称，或者分析某个人对一个公司的评价，像
“ACME Products is good”和“ACME Products is not bad”意思是一样的，即使一句话里用
的是“good”，而另一句话用的是“bad”。

<center>Penn Treebank 语义标记</center>
  
    NLTK 默认使用的语料标记系统是由美国宾夕法尼亚大学大学开发的颇受欢迎的 PennTreebank 项目（http://www.cis.upenn.edu/~treebank/）中的语料标记部分。虽然有些标记意思明确（比如，CC 就是并列连接词，coordinating conjunction），有些标记却比较模糊（比如，RP 是一个小品词，particle）。

除了度量语言，NLTK 还可以用它的超级大字典分析文本内容，帮助人们寻找单词的含义。
NLTK 的一个基本功能是识别句子中各个词的词性：
  
    >>> from nltk.book import *
    >>> from nltk import word_tokenize
    >>> text = word_tokenize("Strange women lying in ponds distributing swords is no
    basis for a system of government. Supreme executive power derives from a mandate
    from the masses, not from some farcical aquatic ceremony.")
    >>> from nltk import pos_tag
    >>> pos_tag(text)
    [('Strange', 'NNP'), ('women', 'NNS'), ('lying', 'VBG'), ('in', 'IN')
    , ('ponds', 'NNS'), ('distributing', 'VBG'), ('swords', 'NNS'), ('is'
    , 'VBZ'), ('no', 'DT'), ('basis', 'NN'), ('for', 'IN'), ('a', 'DT'),
    ('system', 'NN'), ('of', 'IN'), ('government', 'NN'), ('.', '.'), ('S
    upreme', 'NNP'), ('executive', 'NN'), ('power', 'NN'), ('derives', 'N
    NS'), ('from', 'IN'), ('a', 'DT'), ('mandate', 'NN'), ('from', 'IN'),
    ('the', 'DT'), ('masses', 'NNS'), (',', ','), ('not', 'RB'), ('from'
    , 'IN'), ('some', 'DT'), ('farcical', 'JJ'), ('aquatic', 'JJ'), ('cer
    emony', 'NN'), ('.', '.')]
     
每个单词被分开放在一个元组中，一边是单词，一边是 NLTK 识别的词性标记（每个词性
标记的具体含义请参考前面的 Penn Treebank 标记表）。虽然这看起来像是非常简单直接的
查询，但是要正确地完成任务其实很复杂度，用下面的例子看更直观：
    
    >>> text = word_tokenize("The dust was thick so he had to dust")
    >>> pos_tag(text)
    [('The', 'DT'), ('dust', 'NN'), ('was', 'VBD'), ('thick', 'JJ'), ('so
    ', 'RB'), ('he', 'PRP'), ('had', 'VBD'), ('to', 'TO'), ('dust', 'VB')
    ]
   
需要注意的是，“dust”在这句话里出现过两次：一次是名词，而另一次是动词。NLTK
可以基于句子的内容正确地识别出对应的用法。NLTK 用英语的上下文无关文法（context-
free grammar）识别词性。上下文无关文法基本上可以看成一个规则集合，用一个有序的
列表确定一个词后面可以跟哪些词。NLTK 的上下文无关文法确定的是一个词性后面可以
跟哪些词性。无论什么时候，只要遇到像“dust”这样一个含义不明确的单词，NLTK 都
会用上下文无关文法的规则来判断，然后确定一个合适的词性。

<center>机器学习和机器训练</center>
    
    你也可以对 NLTK 进行训练，创建一个全新的上下文无关文法规则，比如，一种外语的上下文无关文法规则。如果你用 Penn Treebank 词性标记手工完成了那种语言的大部分文本的语义标记，那么你就可以把结果传给 NLTK，然后训练它对其他未标记的文本进行语义标记。在任何一个机器学习案例中，机器训练都是不可或缺的部分，这一点我们将在第 11 章训练爬虫识别验证码（CAPTCHA）时介绍。
  
那么，知道某段文字中一个词是动词还是名词有什么用呢？在计算机科学研究室里做研究
可能非常好用，但是它对网络数据采集有什么用呢？

网络数据采集经常需要处理搜索的问题。你在采集了一个网站的文字之后，可能想从文字
里面搜索“google”这个词，但你要的是作为动词的 google，不要作为专用名词的 Google。
或者你就想查找 Google 公司的名称 Google，但是不想通过首字母大写来找出答案（人们
可能忘记将首字母大写，直接写成 google）。那么这时函数 pos_tag 就很管用了：
   
    from nltk import word_tokenize, sent_tokenize, pos_tag
    sentences = sent_tokenize("Google is one of the best companies in the world.
    I constantly google myself to see what I'm up to.")
    nouns = ['NN', 'NNS', 'NNP', 'NNPS']

    for sentence in sentences:
        if "google" in sentence.lower():
            taggedWords = pos_tag(word_tokenize(sentence))
                for word in taggleWords:
                    if word[0].lower() == "google" and word[1] in nouns:
                        print(sentence)
     
这段代码只会打印包含单词“google”（或“Google”）作为名词而非动词的句子。当然，
你也可以更明确地要求只打印标记是“NNP”（专用名词）的“Google”，但是 NLTK 有时
也会判断错误，所以最好还是给自己留一些余地，具体情况由项目而定。

自然语言中的许多歧义问题都可以用 NLTK 的 pos_tag 解决。不只是搜索目标单词或短
语，而是搜索带标记的目标单词或短语，这样可以大大提高爬虫搜索的准确率和效率。

    
           
           `,
        };
      }
    render() {
        return (
            <div >
                {/* 111111111111111
                <Py112 /> */}
                <MarkdownPreview value={this.state.value}/>
            </div>
        )
    }
}

export default Py22